﻿#region Using directives
using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using gpsMe.Utils;
using System.Xml;
using System.Collections;
#endregion

namespace gpsMe.Classes
{
    /// <summary>
    /// gpsMe custom map generated from XML
    /// </summary>
    public class gpsMeMap
    {
        #region Instance
        /// <summary>
        /// Current gpsMeMap instance
        /// </summary>
        private static gpsMeMap instance;

        /// <summary>
        /// Specifies if the gpsMeMap is already instanciated
        /// </summary>
        private static bool isInstanciated;

        /// <summary>
        /// Current gpsMeMap instance accessor
        /// </summary>
        public static gpsMeMap Instance
        {
            get
            {
                if (!isInstanciated)
                {
                    new gpsMeMap();
                    isInstanciated = true;
                }
                return instance;
            }
            private set
            {
                instance = value;
            }
        }
        #endregion

        #region Constants
        /// <summary>
        /// Current version of the gpsMe Xml file format
        /// </summary>
        public const string CURRENT_GPSMEMAP_VERSION = "0.1";

        #region Location modes
        /// <summary>
        /// Specifies a method that can be used to point on the map
        /// </summary>
        public const int LOCATION_MODE_TOP_LEFT_BOTTOM_RIGHT = 0;
        /// <summary>
        /// Specifies a method that can be used to point on the map
        /// </summary>
        public const int LOCATION_MODE_TOP_RIGHT_BOTTOM_LEFT = 1;
        #endregion
        #endregion

        #region Attributes
        #region General informations
        /// <summary>
        /// Specifies if a map is loaded
        /// </summary>
        public bool MapLoaded { get; private set; }

        /// <summary>
        /// Map version of the loaded Xml
        /// </summary>
        public string MapVersion { get; private set; }

        /// <summary>
        /// Name of the loaded map
        /// </summary>
        public string Name { get; private set; }

        /// <summary>
        /// Path of the loaded map where we can find *.map.xml and *.jpeg
        /// </summary>
        public string MapPath { get; private set; }
        #endregion

        #region Location informations
        /// <summary>
        /// Location mode of the map
        /// 0 : TopLeft + BottomRight
        /// 1 : TopRight + BottomLeft
        /// </summary>
        public int LocationMode { get; private set; }
        /// <summary>
        /// Location of the top left side of the map
        /// </summary>
        public gpsMeLocationPoint LocationTopLeft { get; private set; }
        /// <summary>
        /// Location of the top right side of the map
        /// </summary>
        public gpsMeLocationPoint LocationTopRight { get; private set; }
        /// <summary>
        /// Location of the bottom left side of the map
        /// </summary>
        public gpsMeLocationPoint LocationBottomLeft { get; private set; }
        /// <summary>
        /// Location of the bottom right side of the map
        /// </summary>
        public gpsMeLocationPoint LocationBottomRight { get; private set; }
        #endregion

        #region Image informations
        /// <summary>
        /// Specifies the bitmap split mode
        /// true if image files are splitted ("bitmap0.ext", "bitmap1.ext", "bitmap2.ext", etc.)
        /// false if map is in only one image ("bitmap.ext")
        /// </summary>
        public bool BitmapSplit { get; private set; }
        /// <summary>
        /// Total Width of the map in pixels
        /// </summary>
        public int TotalBitmapWidth { get; private set; }
        /// <summary>
        /// Total Height of the map in pixels
        /// </summary>
        public int TotalBitmapHeight { get; private set; }
        /// <summary>
        /// Width of a part of the map
        /// </summary>
        public int BitmapPartWidth { get; private set; }
        /// <summary>
        /// Height of a part of the map
        /// </summary>
        public int BitmapPartHeight { get; private set; }
        /// <summary>
        /// Number of bitmaps of the map horizontally
        /// </summary>
        public int NumberOfBitmapsInWidth { get; private set; }
        /// <summary>
        /// Number of bitmaps of the map vertically
        /// </summary>
        public int NumberOfBitmapsInHeight { get; private set; }
        /// <summary>
        /// Total number of bitmaps of the map
        /// </summary>
        public int NumberOfBitmaps { get; private set; }
        /// <summary>
        /// Common name of the bitmaps
        /// for example if the common name is "bitmap"
        /// the bitmaps will be called "bitmap0.ext", "bitmap1.ext", "bitmap2.ext", etc.
        /// where ".ext" is the bitmap extension
        /// </summary>
        public string BitmapCommonName { get; private set; }
        /// <summary>
        /// Bitmap common extension
        /// Also specifies the type of image that is used
        /// Even if your bitmap is a jpeg file, it will result to a bmp in the software memory
        /// </summary>
        public string BitmapExtension { get; private set; }
        #endregion
        #endregion

        #region Events
        /// <summary>
        /// Invoked when a new map has been loaded
        /// </summary>
        public event EventHandler MapChanged;
        #endregion

        #region Constructor
        /// <summary>
        /// Constructor of gpsMe Map
        /// </summary>
        public gpsMeMap()
        {
            Instance = this;
        }
        #endregion

        #region Methods / Functions / Events
        #region Datas loading
        /// <summary>
        /// Load a new map
        /// </summary>
        /// <param name="p_sMapXmlPath">path of the .map.xml file</param>
        /// <returns>map validity</returns>
        public void LoadMap(string p_sMapXmlPath)
        {
            this.LoadFromXml(p_sMapXmlPath);
            // Setting the map path to it's variable
            this.MapPath = gpsMeUtils.FolderPathFromFilePath(p_sMapXmlPath);
            this.MapLoaded = true;
            if (this.MapChanged != null)
                this.MapChanged(this, new EventArgs());
        }

        /// <summary>
        /// Load datas of the map from Xml
        /// </summary>
        /// <param name="p_sMapXmlPath"></param>
        private void LoadFromXml(string p_sMapXmlPath)
        {
            // Create an instance of XmlTextReader and call Read method to read the file
            XmlTextReader oXmlTextReader = new XmlTextReader(p_sMapXmlPath);
            oXmlTextReader.Read();
            // If the node has value
            while (oXmlTextReader.Read())
            {
                #region gpsMeMap Version
                if (oXmlTextReader.LocalName == "gpsMeMapVersion")
                    this.MapVersion = gpsMeUtils.LoadValueFromXml(ref oXmlTextReader);
                #endregion

                #region gpsMeMap General Informations
                if (oXmlTextReader.LocalName == "gpsMeMapName")
                    this.Name = gpsMeUtils.LoadValueFromXml(ref oXmlTextReader);
                #endregion

                #region gpsMeMap Location Informations
                if (oXmlTextReader.LocalName == "locationMode")
                    this.LocationMode = gpsMeUtils.LoadIntegerValueFromXml(ref oXmlTextReader);
                if (oXmlTextReader.LocalName == "locationTopLeftLatitude")
                {
                    double dLatitude = gpsMeUtils.LoadDoubleValueFromXml(ref oXmlTextReader);
                    while (oXmlTextReader.Read())
                    {
                        if (oXmlTextReader.LocalName == "locationTopLeftLongitude")
                        {
                            break;
                        }
                    }
                    double dLongitude = gpsMeUtils.LoadDoubleValueFromXml(ref oXmlTextReader);
                    this.LocationTopLeft = new gpsMeLocationPoint(dLatitude, dLongitude);
                }
                if (oXmlTextReader.LocalName == "locationTopRightLatitude")
                {
                    double dLatitude = gpsMeUtils.LoadDoubleValueFromXml(ref oXmlTextReader);
                    while (oXmlTextReader.Read())
                    {
                        if (oXmlTextReader.LocalName == "locationTopRightLongitude")
                        {
                            break;
                        }
                    }
                    double dLongitude = gpsMeUtils.LoadDoubleValueFromXml(ref oXmlTextReader);
                    this.LocationTopRight = new gpsMeLocationPoint(dLatitude, dLongitude);
                }
                if (oXmlTextReader.LocalName == "locationBottomLeftLatitude")
                {
                    double dLatitude = gpsMeUtils.LoadDoubleValueFromXml(ref oXmlTextReader);
                    while (oXmlTextReader.Read())
                    {
                        if (oXmlTextReader.LocalName == "locationBottomLeftLongitude")
                        {
                            break;
                        }
                    }
                    double dLongitude = gpsMeUtils.LoadDoubleValueFromXml(ref oXmlTextReader);
                    this.LocationBottomLeft = new gpsMeLocationPoint(dLatitude, dLongitude);
                }
                if (oXmlTextReader.LocalName == "locationBottomRightLatitude")
                {
                    double dLatitude = gpsMeUtils.LoadDoubleValueFromXml(ref oXmlTextReader);
                    while (oXmlTextReader.Read())
                    {
                        if (oXmlTextReader.LocalName == "locationBottomRightLongitude")
                        {
                            break;
                        }
                    }
                    double dLongitude = gpsMeUtils.LoadDoubleValueFromXml(ref oXmlTextReader);
                    this.LocationBottomRight = new gpsMeLocationPoint(dLatitude, dLongitude);
                }
                #endregion

                #region Image informations
                if (oXmlTextReader.LocalName == "bitmapSplit")
                    this.BitmapSplit = gpsMeUtils.LoadBooleanValueFromXml(ref oXmlTextReader);
                if (oXmlTextReader.LocalName == "totalBitmapWidth")
                    this.TotalBitmapWidth = gpsMeUtils.LoadIntegerValueFromXml(ref oXmlTextReader);
                if (oXmlTextReader.LocalName == "totalBitmapHeight")
                    this.TotalBitmapHeight = gpsMeUtils.LoadIntegerValueFromXml(ref oXmlTextReader);
                if (oXmlTextReader.LocalName == "bitmapPartWidth")
                    this.BitmapPartWidth = gpsMeUtils.LoadIntegerValueFromXml(ref oXmlTextReader);
                if (oXmlTextReader.LocalName == "bitmapPartHeight")
                    this.BitmapPartHeight = gpsMeUtils.LoadIntegerValueFromXml(ref oXmlTextReader);
                if (oXmlTextReader.LocalName == "bitmapCommonName")
                    this.BitmapCommonName = gpsMeUtils.LoadValueFromXml(ref oXmlTextReader);
                if (oXmlTextReader.LocalName == "bitmapExtension")
                {
                    this.BitmapExtension = gpsMeUtils.LoadValueFromXml(ref oXmlTextReader);
                    if (this.BitmapExtension[0] != '.')
                        this.BitmapExtension = '.' + this.BitmapExtension;
                }
                #endregion
            }
            oXmlTextReader.Close();
            if (this.BitmapSplit)
            {
                /* Computing of the number of bitmaps in width and height of the image grid */
                if ((this.TotalBitmapWidth % this.BitmapPartWidth) != 0)
                    this.NumberOfBitmapsInWidth = (this.TotalBitmapWidth / this.BitmapPartWidth) + 1;
                else
                    this.NumberOfBitmapsInWidth = this.TotalBitmapWidth / this.BitmapPartWidth;
                if ((this.TotalBitmapHeight % this.BitmapPartHeight) != 0)
                    this.NumberOfBitmapsInHeight = (this.TotalBitmapHeight / this.BitmapPartHeight) + 1;
                else
                    this.NumberOfBitmapsInHeight = this.TotalBitmapHeight / this.BitmapPartHeight;
                this.NumberOfBitmaps = this.NumberOfBitmapsInWidth * this.NumberOfBitmapsInHeight;
            }
        }
        #endregion

        #region Static methods
        /// <summary>
        /// Returns the xml common name
        /// </summary>
        /// <param name="p_sMapXmlPath">path of the .map.xml file</param>
        /// <returns>Name of the map if versions matches</returns>
        public static string GetNameFromXml(string p_sMapXmlPath)
        {
            string name = "";
            string mapVersion = "";
            // Create an instance of XmlTextReader and call Read method to read the file
            XmlTextReader oXmlTextReader = new XmlTextReader(p_sMapXmlPath);
            oXmlTextReader.Read();
            // If the node has value
            while (oXmlTextReader.Read())
            {
                #region gpsMeMap Version
                if (oXmlTextReader.LocalName == "gpsMeMapVersion")
                    mapVersion = gpsMeUtils.LoadValueFromXml(ref oXmlTextReader);
                #endregion

                #region gpsMeMap General Informations
                if (oXmlTextReader.LocalName == "gpsMeMapName")
                    name = gpsMeUtils.LoadValueFromXml(ref oXmlTextReader);
                #endregion
                // Free a few processor time (^_^)
                if ((name != "") && (mapVersion != ""))
                    break;
            }
            oXmlTextReader.Close();
            if ((mapVersion == gpsMeMap.CURRENT_GPSMEMAP_VERSION) && (name != ""))
            {
                return name;
            }
            else
            {
                return null;
            }
        }
        #endregion
        #endregion
    }
}
